//--------------Digen.cpp---------------------------------
#include "ktvInc.h"


// This is the struct that holds state variables that we need. 
typedef struct { 
	short *buff_in_left;
	short *buff_in_right;
	int buff_in_size;
	int pos_in_s;
	int pos_in_e;
	short *buff_out_left;
	short *buff_out_right;
	int buff_out_size;
	int pos_out_s;
	int pos_out_e;
	double pitch_shift;

	int chunk_size;
	int chunk_needed;
	int channels;
	 
} userDataStruct;
 
void *create(long numChannels, int chunkSize, int pitchShift)
{ 
	/* create a instance */
	// init userDataStruct
	userDataStruct *state = new userDataStruct(); 
	state->buff_in_size = MAX_BUFF_SIZE;
	state->buff_in_left = new short[state->buff_in_size];
	state->buff_in_right = new short[state->buff_in_size];
	memset(state->buff_in_left, 0, sizeof(short)*state->buff_in_size);
	memset(state->buff_in_right, 0, sizeof(short)*state->buff_in_size);
	state->pos_in_s = 0;
	state->pos_in_e = 0; 
	state->buff_out_size = MAX_BUFF_SIZE;
	state->buff_out_left = new short[state->buff_out_size];
	state->buff_out_right = new short[state->buff_out_size];
	memset(state->buff_out_left, 0, sizeof(short)*state->buff_out_size);
	memset(state->buff_out_right, 0, sizeof(short)*state->buff_out_size);
	state->pos_out_s = 0;
	state->pos_out_e = 0;
	state->channels = numChannels;

	state->chunk_size = chunkSize * 8;
	state->chunk_needed = state->chunk_size * 2;
	   
	// Here we set our pitch manipulation values 
	state->pitch_shift = pow(2., (float)pitchShift/12.);     // pitch shift (0 semitones) (0.5~1~2) 
 
	return state;
}
 
long process(short *audioOut, long numFrames, void *userData)
{ 
	int i, j;
	userDataStruct *state = (userDataStruct*)userData;
	// save data
	setData(audioOut, numFrames, userData);
	// process data
	// 从in中取出的chunk_needed长度
	if (state->pos_in_e >= state->pos_in_s && state->pos_in_e - state->pos_in_s < state->chunk_needed || 
		state->pos_in_e < state->pos_in_s && state->pos_in_e + state->buff_in_size - state->pos_in_s < state->chunk_needed)
	{
		// not enough data
		i = 0;
	}
	else 
	{
		// has enough data
		int need_chunk_size = state->pitch_shift * state->chunk_size; 
		short *tmp_left_list = new short[state->chunk_size];
		memset(tmp_left_list, 0, sizeof(short)*state->chunk_size);
		//short *tmp_right_list = new short[state->chunk_size];
		//memset(tmp_right_list, 0, sizeof(short)*state->chunk_size);

		_fillChunkData(state, tmp_left_list, need_chunk_size, state->buff_in_left);
		_dataIn2Out(state, tmp_left_list, state->chunk_size, state->buff_out_left);	
		//if ( state->channels == 2 )
		//{
		//	_fillChunkData(state, tmp_right_list, need_chunk_size, state->buff_in_right);
		//	_dataIn2Out(state, tmp_right_list, state->chunk_size, state->buff_out_right);	
		//}
		// update
		state->pos_in_s = (state->pos_in_s + state->chunk_size / 2 ) % state->buff_in_size; 
		state->pos_out_e = (state->pos_out_e + state->chunk_size / 2 ) % state->buff_out_size; 
		//
		delete []tmp_left_list;
		//delete []tmp_right_list;
	}
	 
	// get data
	if ( state->pos_out_e > state->pos_out_s && state->pos_out_e - state->pos_out_s > numFrames ||
		 state->pos_out_e < state->pos_out_s && state->pos_out_e + state->buff_out_size - state->pos_out_s > numFrames )
	{
		for ( i = 0; i < numFrames; i++ )
		{
			if ( state->channels == 1 )
			{
				// mono
				audioOut[i] = state->buff_out_left[(state->pos_out_s + i)%state->buff_out_size];// * MAX_AMP;
			}
			else if ( state->channels == 2 )
			{
				// stereo
				audioOut[i*2] = state->buff_out_left[(state->pos_out_s + i)%state->buff_out_size];// * MAX_AMP;
				//audioOut[i*2 + 1] = state->buff_out_right[(state->pos_out_s + i)%state->buff_out_size] * MAX_AMP;
				audioOut[i*2 + 1] = audioOut[i*2];
			}
		}
		// update
		state->pos_out_s = (state->pos_out_s + numFrames ) % state->buff_out_size; 

	}
	else
	{	
		memset(audioOut, 0, sizeof(short) * numFrames * state->channels);
	}
	 
	return numFrames;
} 

void _dataIn2Out(void *userData, short *in_data, int in_len, short *out_data) 
{
	/* copy data from in to out */
	int i;
	userDataStruct *state = (userDataStruct*)userData;
	
	// humming window   
	//_hanning(in_data, in_len);
	_hamming(in_data, in_len); 

	for(i = 0; i < in_len; i++)
	{
		if( i < state->chunk_size / 2 )
			out_data[(state->pos_out_s + i)%state->buff_out_size] += in_data[i]; 
		else
			out_data[(state->pos_out_s + i)%state->buff_out_size] = in_data[i]; 
	} 
}

void setData(short *audioOut, long numFrames, void *userData)
{ 
	/* save data to buff_in */
	userDataStruct *state = (userDataStruct*)userData;
	// copy chunk data
	int i;
	if ( state->channels == 1 )
	{
		// for mono
		for ( i = 0; i < numFrames; i++ )
		{
			// short->float
			state->buff_in_left[(state->pos_in_e + i)%state->buff_in_size] = (short)((float)audioOut[i]*VOLUME);
		}
	}
	else
	{
		// for stereo
		for ( i = 0; i < numFrames; i++ )
		{
			// short->float
			state->buff_in_left[(state->pos_in_e + i)%state->buff_in_size] = (short)((float)audioOut[i*2]*VOLUME); 
			state->buff_in_right[(state->pos_in_e + i)%state->buff_in_size] = (short)((float)audioOut[i*2+1]*VOLUME); 
		}
	}
	// update pos_of_end
	state->pos_in_e = (state->pos_in_e + numFrames) % state->buff_in_size;
} 

 
void changeParam(void *userData, int pitchShift)
{  
	userDataStruct *state = (userDataStruct*)userData; 
	state->pitch_shift = pow(2., pitchShift/12.);     // pitch shift (0 semitones)
}


void destroyData(void *userData)
{ 
	userDataStruct *state = (userDataStruct*)userData;
	delete[] state->buff_in_left; 
	delete[] state->buff_in_right; 
	delete[] state->buff_out_left; 
	delete[] state->buff_out_right; 
} 
void _hanning(short *wave, int framesize)
{
	int i;
	float a;

	a = M_PI * 2. / (framesize - 1);
	for(i = 0; i < framesize; i++)
		wave[i] *= 0.5 * (1 - cos(i * a)); 
}
void _hamming(short *wave, int framesize) 
{
	int i;
	float a;
	a = M_PI * 2. / (framesize - 1);
	 
	for(i = 0; i < framesize; i++)
		wave[i] *= 0.54 - 0.46 * cos(a * i);
}

void _fillChunkData(void *userData, short *tmp_list, int size, short *in_list)
{
	int i, j;
	float interval;
	float total_inter;
	userDataStruct *state = (userDataStruct*)userData;
	// start copy
	if ( state->pitch_shift > 1. )
	{
		// 升调，抠值，变短: 每隔interval个点抠除一个
		interval = (float)size / (size - (state->chunk_size));
		total_inter = interval;
		for ( i = 0, j = 0; i < size; i++ )
		{
			if ( i + 1 >= total_inter )
			{ 
				total_inter += interval;
			}
			else
			{
				if ( j < (state->chunk_size))
				{
					tmp_list[j] = in_list[(state->pos_in_s+i)%state->buff_in_size]; 
					j++;
				}else{
					//printf("Error1: %d > %d\n", j, state->chunk_size); 
				}
			}
		}
	}
	else if ( state->pitch_shift < 1. )
	{
		// 降调，插值，变长：每隔interval个点插入一个
		interval = (float)size / ((state->chunk_size) - size);
		total_inter = interval;
		for ( i = 0, j = 0; i < size; i++ )
		{
			if ( j < (state->chunk_size))
			{
				tmp_list[j] = in_list[(state->pos_in_s+i)%state->buff_in_size]; 
				j++;
			}else{
				printf("Error2: %d > %d\n", j, state->chunk_size); 
			}
			if ( i + 1 >= total_inter )
			{ 
				if ( j < (state->chunk_size))
				{
					tmp_list[j] = in_list[(state->pos_in_s+i)%state->buff_in_size]; 
					j++;
					total_inter += interval;
				}else{
					printf("Error: %d > %d\n", j, state->chunk_size); 
				}
			} 
		}
	}
	else
	{
		// 不升不降
		for ( i = 0; i < size; i++ )
		{
			tmp_list[i] = in_list[(state->pos_in_s+i)%state->buff_in_size]; 

		}
	}
}

/* not used */
void reset(void *userData)
{ 
	userDataStruct *state = (userDataStruct*)userData; 
	state->pos_in_s = state->pos_in_e = 0; 
	state->pos_out_s = state->pos_out_e = 0;
	memset(state->buff_in_left, 0, sizeof(short) * state->buff_in_size);
	memset(state->buff_in_right, 0, sizeof(short) * state->buff_in_size);
	memset(state->buff_out_left, 0, sizeof(short) * state->buff_out_size);
	memset(state->buff_out_right, 0, sizeof(short) * state->buff_out_size);
}